package com.nbsaas.boot.queue.monitor;

import com.nbsaas.boot.queue.storage.TaskStorage;
import com.nbsaas.boot.queue.task.Task;
import com.nbsaas.boot.queue.task.TaskStatus;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public class TaskMonitor {
    private final TaskStorage taskStorage;
    private final Map<TaskStatus, AtomicLong> statusCounts;
    private final AtomicLong totalTasks;
    private final AtomicLong successfulTasks;
    private final AtomicLong failedTasks;
    private final AtomicLong totalExecutionTime;
    private LocalDateTime startTime;

    public TaskMonitor(TaskStorage taskStorage) {
        this.taskStorage = taskStorage;
        this.statusCounts = new ConcurrentHashMap<>();
        this.totalTasks = new AtomicLong(0);
        this.successfulTasks = new AtomicLong(0);
        this.failedTasks = new AtomicLong(0);
        this.totalExecutionTime = new AtomicLong(0);
        this.startTime = LocalDateTime.now();

        // 初始化状态计数器
        for (TaskStatus status : TaskStatus.values()) {
            statusCounts.put(status, new AtomicLong(0));
        }
    }

    public void taskCreated(Task task) {
        totalTasks.incrementAndGet();
        statusCounts.get(TaskStatus.PENDING).incrementAndGet();
    }

    public void taskStarted(Task task) {
        statusCounts.get(TaskStatus.PENDING).decrementAndGet();
        statusCounts.get(TaskStatus.RUNNING).incrementAndGet();
    }

    public void taskCompleted(Task task) {
        statusCounts.get(TaskStatus.RUNNING).decrementAndGet();
        statusCounts.get(TaskStatus.COMPLETED).incrementAndGet();
        successfulTasks.incrementAndGet();
        updateExecutionTime(task);
    }

    public void taskFailed(Task task) {
        statusCounts.get(TaskStatus.RUNNING).decrementAndGet();
        statusCounts.get(TaskStatus.FAILED).incrementAndGet();
        failedTasks.incrementAndGet();
        updateExecutionTime(task);
    }

    private void updateExecutionTime(Task task) {
        LocalDateTime startTime = task.getMetadata().getStartTime();
        LocalDateTime endTime = task.getMetadata().getEndTime();
        if (startTime != null && endTime != null) {
            long executionTime = ChronoUnit.MILLIS.between(startTime, endTime);
            totalExecutionTime.addAndGet(executionTime);
        }
    }

    public TaskStatistics getStatistics() {
        long total = totalTasks.get();
        long successful = successfulTasks.get();
        long failed = failedTasks.get();
        long avgExecutionTime = total > 0 ? totalExecutionTime.get() / total : 0;

        return new TaskStatistics(
                total,
                successful,
                failed,
                avgExecutionTime,
                getUptime(),
                getStatusCounts()
        );
    }

    private long getUptime() {
        return ChronoUnit.SECONDS.between(startTime, LocalDateTime.now());
    }

    private Map<TaskStatus, Long> getStatusCounts() {
        Map<TaskStatus, Long> counts = new ConcurrentHashMap<>();
        for (Map.Entry<TaskStatus, AtomicLong> entry : statusCounts.entrySet()) {
            counts.put(entry.getKey(), entry.getValue().get());
        }
        return counts;
    }

    public static class TaskStatistics {
        private final long totalTasks;
        private final long successfulTasks;
        private final long failedTasks;
        private final long averageExecutionTime;
        private final long uptimeSeconds;
        private final Map<TaskStatus, Long> statusCounts;

        public TaskStatistics(long totalTasks, long successfulTasks, long failedTasks,
                              long averageExecutionTime, long uptimeSeconds,
                              Map<TaskStatus, Long> statusCounts) {
            this.totalTasks = totalTasks;
            this.successfulTasks = successfulTasks;
            this.failedTasks = failedTasks;
            this.averageExecutionTime = averageExecutionTime;
            this.uptimeSeconds = uptimeSeconds;
            this.statusCounts = statusCounts;
        }

        public long getTotalTasks() {
            return totalTasks;
        }

        public long getSuccessfulTasks() {
            return successfulTasks;
        }

        public long getFailedTasks() {
            return failedTasks;
        }

        public long getAverageExecutionTime() {
            return averageExecutionTime;
        }

        public long getUptimeSeconds() {
            return uptimeSeconds;
        }

        public Map<TaskStatus, Long> getStatusCounts() {
            return statusCounts;
        }

        public double getSuccessRate() {
            return totalTasks > 0 ? (double) successfulTasks / totalTasks * 100 : 0;
        }

        public double getFailureRate() {
            return totalTasks > 0 ? (double) failedTasks / totalTasks * 100 : 0;
        }
    }
} 